package com.hzit.portal.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzit.common.utils.PageUtils;
import com.hzit.common.utils.Query;
import com.hzit.portal.dao.ContentDao;
import com.hzit.portal.entity.ContentEntity;
import com.hzit.portal.service.ContentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;


@Service("contentService")
@Slf4j
public class ContentServiceImpl extends ServiceImpl<ContentDao, ContentEntity> implements ContentService {
	@Autowired
	private StringRedisTemplate redisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<ContentEntity> page = this.page(
                new Query<ContentEntity>().getPage(params),
                new QueryWrapper<ContentEntity>()
        );

        return new PageUtils(page);
    }

    //1. 查询所有广告
	@Override
	public List<ContentEntity> findAll() {
    	//1.0 定义返回的集合对象
		List<ContentEntity> contentEntities = null;
    	//1.1 从redis中取得广告列表
		String contents = redisTemplate.opsForValue().get("contentList");
		//1.2 转换为对象
		if(StrUtil.isNotBlank(contents)){
			System.out.println("缓存命中...");
			contentEntities = JSON.parseArray(contents,ContentEntity.class);
		}else{
			System.out.println("从数据库中取。。。。");
			contentEntities = this.list();
			//将取出的数据放到redis中
			redisTemplate.opsForValue().set("contentList", JSON.toJSONString(contentEntities));
		}
		return contentEntities;
	}

	// 1. 使用springcache缓存框架将数据放到redis中
	@Override
	// @Cacheable: 将数据放到缓存中，会首先检查是否在缓存中存在指定key的数据，如果不存在才查询数据库并放入缓存中，否则，不执行方法体！
	@Cacheable(key = "#root.methodName",value = "contents") // value: 代表命名空间名 key: 代表空间后的后缀名 #root.methodName：是一个spel表达式
	public List<ContentEntity> findAll2() {
		System.out.println("查询数据库。");
		return this.list();
	}

	@Override
	public List<ContentEntity> findAll3() {
		// 1.5.0 版本开始可以利用 try-with-resources 特性（使用有限制）
		// 资源名可使用任意有业务语义的字符串，比如方法名、接口名或其它可唯一标识的字符串。
		try (Entry entry = SphU.entry("hello")) {
			// 被保护的业务逻辑
			return this.list();
		} catch (BlockException ex) {
			// 资源访问阻止，被限流或被降级
			// 在此处进行相应的处理操作
			log.info("出现了异常：{}",ex);
		}
		return null;
	}

	//2. 定义要保护的资源
	// @SentinelResource(value = "contentRes",blockHandler = "contentBlockHandler")
	//① fallback: 处理任意异常，方法定义在此类中，方法的原型定义（返回值与注解所在方法一样，参数也一样，最后一个参数为Throwable类型）
	//② fallbackClass: 主要是不想把处理逻辑定义在同一个类中，并且方法必须是静态方法。此时用它指定类名，同时注意事项同上，并且此时也要加上fallback参数
	//③ blockHandler与blockHandlerClass与上面的两个属性代表的意义一样，只是，它们只处理BlockException异常！
	// @SentinelResource(value="contentRes",fallback = "contentFallback")
	@SentinelResource(value="contentRes",fallbackClass = ContentFallBack.class,fallback = "myFallBack")
	@Override
	public ContentEntity findById(String id) {
    	int i = 10 / 0;
		return this.getById(id);
	}

	@Override
	public String message1() {
		return "message1";
	}

	@Override
	public String message2() {
		return "message2";
	}
	@SentinelResource(value="message",blockHandler = "contentBlockHandler")
	@Override
	public String message() {
		return "message";
	}
	public String contentBlockHandler(BlockException e){
    	log.error("出现了限流异常[资源message]");
    	return "";
	}
	//2.1 对资源contentRes的保护
	public ContentEntity contentBlockHandler(String id,BlockException e) {
    	log.info("id：{}，出现了限流异常！",id);
		return null;
	}

	public ContentEntity contentFallback(String id,Throwable e) {
		log.info("id：{}，出现了{}异常！",id,e.getClass());
		return null;
	}

}